package com.goldsprite.gesturerecognizer.core;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class GestureRecognizer {
	private static final float MIN_DISTANCE = 5.0f;//录入最小点间距
	private static final float ANGLE_THRESHOLD = 25.0f;//录入角度差阈值
	private static final float MATCH_THRESHOLD = 70/100f;//匹配相似度

	private Gesture currentGesture;
	private boolean isRecording;
	private Map<String, Gesture> gestureLibrary;

	public GestureRecognizer() {
		this.isRecording = false;
		this.gestureLibrary = new HashMap<>();
	}

	public void addGestureToLibrary(String name, Gesture gesture) {
		gestureLibrary.put(name, gesture);
	}

	public Gesture getGestureFromLibrary(String name) {
		return gestureLibrary.get(name);
	}

	public List<String> getGestureNames() {
		return new ArrayList<>(gestureLibrary.keySet());
	}

	public void removeGestureFromLibrary(String name) {
		gestureLibrary.remove(name);
	}

	public void startRecording(String gestureName) {
		this.currentGesture = new Gesture(gestureName);
		this.isRecording = true;
	}

	public void addPoint(float x, float y) {
		if (!isRecording || currentGesture == null) return;

		GesturePoint newPoint = new GesturePoint(x, y);

		if (currentGesture.getPointCount() == 0) {
			currentGesture.addPoint(newPoint);
		} else {
			GesturePoint lastPoint = currentGesture.getPoint(currentGesture.getPointCount() - 1);
			float distance = lastPoint.distanceTo(newPoint);

			if (distance >= MIN_DISTANCE) {
				if (currentGesture.getPointCount() == 1) {
					currentGesture.addPoint(newPoint);
				} else {
					GesturePoint prevPoint = currentGesture.getPoint(currentGesture.getPointCount() - 2);
					float prevAngle = calculateAngle(prevPoint, lastPoint);
					float currentAngle = calculateAngle(lastPoint, newPoint);
					float angleDiff = Math.abs(normalizeAngle(currentAngle - prevAngle));

					if (angleDiff >= ANGLE_THRESHOLD) {
						currentGesture.addPoint(newPoint);
					} else {
						currentGesture.getPoints().set(currentGesture.getPointCount() - 1, newPoint);
					}
				}
			}
		}
	}

	public Gesture finishRecording() {
		if (!isRecording || currentGesture == null) return null;

		isRecording = false;
		Gesture result = currentGesture;
		currentGesture = null;
		return result;
	}

	public boolean isRecording() {
		return isRecording;
	}

	public Gesture getCurrentGesture() {
		return currentGesture;
	}

	public boolean matchGestures(Gesture gesture1, Gesture gesture2) {
		if (gesture1 == null || gesture2 == null) return false;

		if (gesture1.getPointCount() != gesture2.getPointCount()) {
			return false;
		}

		Gesture normalized1 = gesture1.normalize();
		Gesture normalized2 = gesture2.normalize();

		int matchCount = 0;
		int totalComparisons = normalized1.getPointCount() - 1;

		for (int i = 0; i < totalComparisons; i++) {
			GesturePoint p11 = normalized1.getPoint(i);
			GesturePoint p12 = normalized1.getPoint(i + 1);
			GesturePoint p21 = normalized2.getPoint(i);
			GesturePoint p22 = normalized2.getPoint(i + 1);

			float angle1 = calculateAngle(p11, p12);
			float angle2 = calculateAngle(p21, p22);
			float angleDiff = Math.abs(normalizeAngle(angle1 - angle2));

			float dist1 = p11.distanceTo(p12);
			float dist2 = p21.distanceTo(p22);
			float distRatio = (dist1 > 0 && dist2 > 0) ?
				Math.min(dist1, dist2) / Math.max(dist1, dist2) : 1.0f;

			if (angleDiff < ANGLE_THRESHOLD && distRatio > 0.8f) {
				matchCount++;
			}
		}

		float matchRatio = (float) matchCount / totalComparisons;
		return matchRatio >= MATCH_THRESHOLD;
	}

	private float calculateAngle(GesturePoint p1, GesturePoint p2) {
		float dx = p2.x - p1.x;
		float dy = p2.y - p1.y;
		return (float) Math.toDegrees(Math.atan2(dy, dx));
	}

	private float normalizeAngle(float angle) {
		while (angle > 180) angle -= 360;
		while (angle < -180) angle += 360;
		return angle;
	}
}
